﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

namespace Equinoxe.EZNFC.Library.Windows
{
    /// <summary>
    /// Observable trace listener
    /// </summary>
    /// <remarks></remarks>
    public class ObservableTraceListener : TraceListener
    {

        /// <summary>
        /// The observable trace.
        /// </summary>
        /// <value>The trace.</value>
        /// <remarks></remarks>
        public ObservableTrace Trace { get; set; }


        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.MarshalByRefObject"/> class.
        /// </summary>
        /// <remarks></remarks>
        public ObservableTraceListener()
        {
            Trace = new ObservableTrace();
        }

        /// <summary>
        /// When overridden in a derived class, writes the specified message to the listener you create in the derived class.
        /// </summary>
        /// <param name="message">A message to write.</param>
        /// <remarks></remarks>
        public override void Write(string message)
        {
            Trace.NewMessage(message);
        }

        /// <summary>
        /// When overridden in a derived class, writes a message to the listener you create in the derived class, followed by a line terminator.
        /// </summary>
        /// <param name="message">A message to write.</param>
        /// <remarks></remarks>
        public override void WriteLine(string message)
        {
            Trace.NewMessage(Environment.NewLine + message);
        }
    }

    /// <summary>
    /// Observable trace. 
    /// </summary>
    /// <remarks></remarks>
    public class ObservableTrace : IObservable<string>
    {
        /// <summary>
        /// Registered observers
        /// </summary>
        readonly List<WeakReference> _registeredobservers = new List<WeakReference>();

        /// <summary>
        /// Register an observer.
        /// </summary>
        /// <param name="observer">The observer.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        public IDisposable Subscribe(IObserver<string> observer)
        {
            // todo: change with key
            if (!_registeredobservers.Where(d => d.IsAlive).Select(d => d.Target as IObserver<string>).Contains(observer))
                _registeredobservers.Add(new WeakReference(observer));

            return new DisposableClass(_registeredobservers, observer);
        }

        /// <summary>
        /// News the message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <remarks></remarks>
        public void NewMessage(string message)
        {
            foreach (var observer in _registeredobservers.Where(d => d.IsAlive).Select(d => d.Target as IObserver<string>))
            {
                if (message != null)
                    observer.OnNext(message);
            }
        }

        /// <summary>
        /// Ends this instance.
        /// </summary>
        /// <remarks></remarks>
        public void End()
        {
            foreach (var observer in _registeredobservers.Where(d => d.IsAlive).Select(d => d.Target as IObserver<string>).ToList())
            {
                observer.OnCompleted();
            }

            _registeredobservers.Clear();
        }

        /// <summary>
        /// Simple disposable class
        /// </summary>
        /// <remarks></remarks>
        private class DisposableClass : IDisposable
        {
            /// <summary>
            /// Observer list
            /// </summary>
            private readonly IList<WeakReference> _observerlist;
            /// <summary>
            /// Linked observer
            /// </summary>
            private readonly WeakReference _observer;


            /// <summary>
            /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
            /// </summary>
            /// <remarks></remarks>
            public void Dispose()
            {
                if ((_observer != null) && (_observerlist.Where(d => d.IsAlive).Select(d => d.Target as IObserver<string>).Contains(_observer.Target)))
                    _observerlist.Remove(_observer);
            }

            /// <summary>
            /// Initializes a new instance of the <see cref="DisposableClass"/> class.
            /// </summary>
            /// <param name="observerlist">The observerlist.</param>
            /// <param name="observer">The observer.</param>
            /// <remarks></remarks>
            public DisposableClass(IList<WeakReference> observerlist, IObserver<string> observer)
            {
                _observer = new WeakReference(observer);
                _observerlist = observerlist;
            }
        }


    }

    /// <summary>
    /// Observer
    /// </summary>
    /// <remarks></remarks>
    public class ObservableTraceObserver : IObserver<string>
    {
        /// <summary>
        /// Reference to disposable object
        /// </summary>
        private IDisposable _disposableobject;

        /// <summary>
        /// On next action
        /// </summary>
        private readonly Action<string> _action_on_next;

        /// <summary>
        /// On exception
        /// </summary>
        private readonly Action<Exception> _action_on_exception;

        /// <summary>
        /// On completed
        /// </summary>
        private readonly Action _action_on_completed;


        /// <summary>
        /// Initializes a new instance of the <see cref="ObservableTraceObserver"/> class.
        /// </summary>
        /// <param name="onNext">The onnext.</param>
        /// <param name="onException">The onexception.</param>
        /// <param name="onCompleted">The oncompleted.</param>
        /// <remarks></remarks>
        public ObservableTraceObserver(Action<string> onNext, Action<Exception> onException = null, Action onCompleted = null)
        {
            _action_on_next = onNext;

            _action_on_exception = onException;

            _action_on_completed = onCompleted;
        }


        /// <summary>
        /// Subscribes the specified messageprovider.
        /// </summary>
        /// <param name="messageprovider">The messageprovider.</param>
        /// <remarks></remarks>
        public void Subscribe(IObservable<string> messageprovider)
        {
            if (messageprovider != null)
                _disposableobject = messageprovider.Subscribe(this);
        }

        /// <summary>
        /// Uns the subscribe.
        /// </summary>
        /// <remarks></remarks>
        public void UnSubscribe()
        {
            if (_disposableobject != null)
                _disposableobject.Dispose();
        }

        /// <summary>
        /// Called when next value pushed.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <remarks></remarks>
        public void OnNext(string value)
        {
            if (_action_on_next != null)
                _action_on_next(value);
        }

        /// <summary>
        /// Called when an error occurs.
        /// </summary>
        /// <param name="error">The error.</param>
        /// <remarks></remarks>
        public void OnError(Exception error)
        {
            if (_action_on_next != null)
                _action_on_exception(error);
        }

        /// <summary>
        /// Called when completed.
        /// </summary>
        /// <remarks></remarks>
        public void OnCompleted()
        {
            if (_action_on_next != null)
                _action_on_completed();
        }
    }

}
